// nodejs是一个事件驱动的
// 使用nodejs内置的http模块完成 web服务器 tcp
const http = require('http')
const path = require('path')
const fs = require('fs')
// 加密模块
const crypto = require('crypto')
// 得到一个webserver对象
const server = http.createServer();

// 定义好对应的mime类型
let mimeTypes = require('./libs/mime')

// 要缓存的类型
let expireType = ['jpg', 'jpeg', 'gif']

// 定义网站根目录  相对转为绝对   浏览器中访问的 / => 磁盘中的此路径
const webRoot = path.resolve('public')

// 监听客户发送过来的请求
server.on('request', (req, res) => {
  // 获取当前请求的url地址
  // 缺省值处理
  let pathname = req.url == '/' ? '/index.html' : req.url

  // 对于ico请求不做任何处理
  if (pathname != '/favicon.ico') {
    // 请求的url地址和本机服务器中存在的文件进行映射, 得到访问文件路径(绝对路径)
    var filepath = path.join(webRoot, pathname)

    // 得到路径中的扩展名
    let extname = path.extname(filepath).slice(1)
    // 短语运算
    let mime = mimeTypes[extname] || 'text/html;charset=utf-8'

    // 判断当前文件是否存在
    if (fs.existsSync(filepath)) {
      // 设置响应头中的mime类型
      res.setHeader('content-type', mime)

      // if(expireType.includes(extname)){}
      if (/(png|jpe?g|gif|ico)/i.test(extname)) {
        // 设置强缓存
        // http1.0
        res.setHeader('expires', new Date(Date.now() + 3600 * 1000).toGMTString())
        // http1.1
        res.setHeader('cache-control', 'max-age=' + 3600) 

        // 设置协商缓存
        // http1.0
        // 当前访问的文件修改时间
        let mtime = fs.statSync(filepath).mtime.toGMTString()
        // 接受浏览器通过头信息发过来的数据,时间
        let lastTime = req.headers['if-modified-since']
        // 浏览器发送过来的时间和当前文件修改时间如果一致则返回304
        if (mtime == lastTime) {
          res.statusCode = 304
          res.end('')
          return;
        }
        // 第1次或有修改则发送文件修改时间给浏览器
        res.setHeader('last-modified', mtime)

        // http1.1
        // 读取文件内容进行md5加密
        let hashFile = crypto.createHash('md5').update(fs.readFileSync(filepath)).digest('hex')
        // 获取客户端发过来的协商缓存头信息
        let etag = req.headers['if-none-match']
        if (hashFile == etag) {
          res.statusCode = 304
          res.end('')
          return;
        }
        res.setHeader('Etag', hashFile)
      }

      // 读取磁盘中存在的文件，显示
      res.end(fs.readFileSync(filepath))

    } else {
      var filepath = path.join(webRoot, '/404.html')
      // 设置响应头中的mime类型
      res.setHeader('content-type', mime)
      res.statusCode = 404
      // 响应体
      res.end(fs.readFileSync(filepath))
    }
  }
});

// 绑定主机和端口
// 参数1：绑定的端口号
// 参数2：可选，绑定服务器中的网卡 127.0.0.1只能本机访问 0.0.0.0绑定本机中所有的网卡,如果不写以ipv6的方式让所有的网卡绑定上，缺点获取的ip地址，将不对
server.listen(3000, '127.0.0.1', () => console.log('服务器启动'))


